#ifndef LOG_H
#define LOG_H 1

#include <errno.h>
#include <stdint.h>
#include <string.h>

#define LOG_BUFLEN 256
#define LOGLVL_STRLEN 5

#define LOG(lvl, fmt, ...) log_to(__FILE__, __LINE__, lvl, fmt, __VA_ARGS__);

#define E(fmt, ...) log_to(__FILE__, __LINE__, log_err, fmt, __VA_ARGS__);
#define E2(str) E("%s", str)
#define E_errstr(fmt, ...) log_to(__FILE__, __LINE__, log_err, fmt, __VA_ARGS__); \
                           log_to(__FILE__, __LINE__, log_err, "  Error(%d): %s", errno, strerror(errno));
#define E2_errstr(str) E_errstr("%s", str)

#define W(fmt, ...) log_to(__FILE__, __LINE__, log_warn, fmt, __VA_ARGS__);
#define W2(str) W("%s", str)
#define W_errstr(fmt, ...) log_to(__FILE__, __LINE__, log_warn, fmt, __VA_ARGS__); \
                           log_to(__FILE__, __LINE__, log_warn, "  Error(%d): %s", errno, strerror(errno));
#define W2_errstr(str) W_errstr("%s", str)

#define I(fmt, ...) log_to(__FILE__, __LINE__, log_info, fmt, __VA_ARGS__);
#define I2(str) log_to(__FILE__, __LINE__, log_info, str, NULL);
#define D(fmt, ...) log_to(__FILE__, __LINE__, log_debug, fmt, __VA_ARGS__);
#define D2(str) log_to(__FILE__, __LINE__, log_debug, str, NULL);

#define EF(fmt, ...) E("%s(): " fmt, __FUNC__, __VA_ARGS__)
#define EF_errstr(fmt, ...) E_errstr("%s(): " fmt, __FUNC__, __VA_ARGS__)

#ifdef ENABLE_DEBUG
# define DD(fmt, ...) log_to(__FILE__, __LINE__, log_debug, fmt, __VA_ARGS__);
# define DD2(str) log_to(__FILE__, __LINE__, log_debug, str, NULL);
#else
# define DD(fmt, ...)
# define DD2(str)
#endif

#ifdef ENABLE_REQDBG
# define RD(fmt, ...) D(fmt, __VA_ARGS__)
# define RD2(str) D2(str)
#else
# define RD(fmt, ...)
# define RD2(str)
#endif

typedef enum log_target {
  log_stderr,
  log_file,
  log_syslog,
  log_null
} log_target_t;

typedef enum log_level {
   log_err = 3,
   log_warn = 2,
   log_info = 1,
   log_debug = 0,
   log_not = -1
} log_level_t;

void
log_to(const char *__FILE, const uint16_t __LINE, log_level_t __lvl, const char *__fmt, ...);

int
set_log_opts(const log_target_t __log_tgt, const log_level_t __log_lvl);

int
log_init(const void *__log_arg);

void
log_end(void);

#endif
